package com.sunop.pantiproblem.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sunop.panticommon.entity.PageResult;
import com.sunop.panticommon.entity.Result;
import com.sunop.pantiproblem.mapper.ProblemMapper;
import com.sunop.pantiproblem.model.Problem;
import com.sunop.pantiproblem.model.ProblemDocument;
import com.sunop.pantiproblem.model.ProblemPageQueryDTO;
import com.sunop.pantiproblem.model.ProblemVO;
import com.sunop.pantiproblem.properties.ProblemRepository;
import com.sunop.pantiproblem.service.ProblemService;
import com.sunop.pantiproblem.util.DockerUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Set;
import java.util.UUID;

import java.util.Arrays;
import java.util.List;

/**
 * @since 2025-03-18
 */
@Service
@Slf4j
@AllArgsConstructor
public class ProblemServiceImpl extends ServiceImpl<ProblemMapper, Problem> implements ProblemService {

    private final ProblemMapper problemMapper;
    private final RabbitTemplate rabbitTemplate;
    private final ProblemRepository problemRepository;
    private final DockerUtil dockerUtil;
    private final RedisTemplate redisTemplate;


    @Override
    public PageResult pageQuery(ProblemPageQueryDTO problemPageQuery) {
        if (problemPageQuery.getPageNum() < 1 || problemPageQuery.getPageSize() < 1) {
            throw new IllegalArgumentException("Invalid pageNum or pageSize value");
        }
        List<String> allowedSortBy = Arrays.asList("id", "comment_num", "submit_num", "pass_num");
        List<String> allowedOrderBy = Arrays.asList("asc", "desc");
        if (!allowedSortBy.contains(problemPageQuery.getSortBy())) {
            throw new IllegalArgumentException("Invalid sortBy value: " + problemPageQuery.getSortBy());
        }
        if (!allowedOrderBy.contains(problemPageQuery.getOrderBy())) {
            throw new IllegalArgumentException("Invalid orderBy value: " + problemPageQuery.getOrderBy());
        }
        PageHelper.startPage(problemPageQuery.getPageNum(), problemPageQuery.getPageSize());
        Page<ProblemVO> page = problemMapper.pageQuery(problemPageQuery);
        return new PageResult(page.getTotal(), page.getResult());
    }

    @Override
    public void saveToES(Integer pid, String title, String content) {
        ProblemDocument problem = new ProblemDocument();
        problem.setPid(pid);
        problem.setTitle(title);
        problem.setContent(content);
        problem.setId(pid.toString());
        // 2. 发送消息到 RabbitMQ
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        log.info("Received problem: " + problem);
        rabbitTemplate.convertAndSend(
                "problem.exchange",
                "problem.key",
                problem,
                message -> {
                    message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT); // 消息持久化
                    message.getMessageProperties().setContentType("application/json"); // 设置 contentType
                    return message;
                },
                correlationData
        );
    }
    public List<Integer> getPidByTitle(String title) {
        return problemRepository.findPidByTitle(title);
    }

    @Override
    public List<ProblemVO> pageQueryByTitle(List<Integer> pidByTitle, int pageNum, int pageSize) {
        pageNum=(pageNum-1)*pageSize;// offset
        return problemMapper.pageQueryByTitle(pidByTitle, pageNum, pageSize);
    }

    @Override
    public void setInput(int pid, String language) {
        String input = problemMapper.findInById(pid);
        if (input == null) {
            throw new RuntimeException("题目"+pid+"输入为空");
        }
        String containerID = dockerUtil.getContainId(language);
        JSONObject array = JSONUtil.parseObj(input);
        for(int i = 1; i <= array.size(); i++){
            String value = array.getStr("in"+i);
            log.info("input"+i+": {}", value);//input1: [3,3] input2: [3,2,4] input3: [2,7,11,15]
            String files= DockerUtil.cinFiles+(pid-1000)+"/"+i+".in";
            dockerUtil.modifyFileInput(containerID ,files, value);
        }
        log.info("成功上传测评数据到"+language+"snadbox");
    }

    @Override
    public Result collectProblem(Integer problemId) {
        try {
            // 获取当前用户 ID
            int userId = StpUtil.getLoginIdAsInt();
            // 将题目 ID 添加到用户的有序集合中，Score 为当前时间戳
            double score = System.currentTimeMillis();
            redisTemplate.opsForZSet().add("user:collect:" + userId, problemId, score);
            return Result.success(null, "收藏成功");
        } catch (Exception e) {
            throw new RuntimeException("收藏失败", e);
        }
    }

    @Override
    public Result uncollectProblem(Integer problemId) {
        try {
            // 获取当前用户 ID
            int userId = StpUtil.getLoginIdAsInt();
            // 将题目 ID 从用户的有序集合中移除
            redisTemplate.opsForZSet().remove("user:collect:" + userId, problemId);
            return Result.success(null, "取消收藏成功");
        } catch (Exception e) {
            throw new RuntimeException("取消收藏失败", e);
        }
    }

    @Override
    public Result getCollectedProblems(int page, int size, boolean reverseOrder) {
        try {
            // 获取当前用户 ID
            int userId = StpUtil.getLoginIdAsInt();
            // 计算分页的起始和结束位置
            long start = (page - 1) * size;
            long end = start + size - 1;
            // 根据排序规则获取分页数据
            Set<Integer> problemIds;
            if (reverseOrder) {
                // 倒序：按 Score 从大到小排序
                problemIds = redisTemplate.opsForZSet().reverseRange("user:collect:" + userId, start, end);
            } else {
                // 正序：按 Score 从小到大排序
                problemIds = redisTemplate.opsForZSet().range("user:collect:" + userId, start, end);
            }
            // 返回收藏的题目 ID 列表
            return Result.success(problemIds, "获取收藏列表成功");
        } catch (Exception e) {
            throw new RuntimeException("获取收藏列表失败", e);
        }
    }

    @Override
    public long getCollectedCount() {
        try {
            // 获取当前用户 ID
            int userId = StpUtil.getLoginIdAsInt();
            // 获取用户的有序集合大小
            return redisTemplate.opsForZSet().size("user:collect:" + userId);
        } catch (Exception e) {
            throw new RuntimeException("获取收藏总数失败", e);
        }
    }

    @Override
    public boolean isProblemCollected(Integer problemId) {
        try {
            // 获取当前用户 ID
            int userId = StpUtil.getLoginIdAsInt();
            // 检查题目 ID 是否在用户的有序集合中
            return redisTemplate.opsForZSet().score("user:collect:" + userId, problemId) != null;
        } catch (Exception e) {
            throw new RuntimeException("检查收藏状态失败", e);
        }
    }

    @Override
    public Result problemLike(Integer problemId) {
        try {
            redisTemplate.opsForSet().add("like:"+"problem:" + problemId +":user:"+StpUtil.getLoginIdAsInt(),problemId);
            redisTemplate.opsForValue().increment("like:"+"problem:" + problemId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return Result.success(null,"点赞成功");
    }

    @Override
    public Result problemUnlike(Integer problemId) {
        try {
            redisTemplate.opsForSet().remove("like:"+"problem:" + problemId +":user:"+StpUtil.getLoginIdAsInt(),problemId);
            redisTemplate.opsForValue().decrement("like:"+"problem:" + problemId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return Result.success(null,"取消点赞成功");
    }

    @Override
    public List<Problem> getProblemsByAI(Integer difficulty, String tag) {
        try {
            return List.of(problemMapper.getProblemsByAI(difficulty, tag));
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
        }
    }

}
